搜索 K
Appearance
博客正在加载中...
Appearance
接下来我们用 Lucene 做一个小案例
实现一个文件的搜索功能,通过关键字搜索文件,凡是文件名或文件内容包括关键字的文件都需要找出来。还可以根据中文词语进行查询,并且需要支持多个条件查询。本案例中的原始内容就是磁盘上的文件,如下图:
新建一个 Maven 项目,导入依赖:
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>7.4.0</version>
</dependency>说明:
或者从官网下载最新版:Apache Lucene,历史版本可以在 这里 下载。注意有 Lucene 有很多 jar 包,按需引入
为了方便,我们还引入 commons-io 和 Junit
步骤如下:
然后我们创建 D:\temp\searchsource 目录,里面存放我们的原始文档(可以在我的 Gitee 或 GitHub 上下载) 第一步:创建一个 Director 对象,指定索引库保存的位置。
Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath()); 第二步:基于 Directory 对象创建一个 IndexWriter 对象(用来写索引),需要传入一个配置对象 IndexWriterConfig,这里我们用默认的。
IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig()); 第三、四步:读取磁盘上的文件,对应每个文件创建一个文档对象:
File dir = new File("D:\\temp\\searchsource");
File[] files = dir.listFiles();
for(File file : files) {
String fileName = file.getName();
String filePath = file.getPath();
String fileContent = FileUtils.readFileToString(file, "utf-8");
long fileSize = FileUtils.sizeOf(file);
// 创建Field
// 参数1:域的名称, 参数2:域的内容, 参数3:是否存储
Field fieldName = new TextField("name", fileName, Field.Store.YES);
Field fieldPath = new TextField("path", filePath, Field.Store.YES);
Field fieldContent = new TextField("content", fileContent, Field.Store.YES);
Field fieldSize = new TextField("size", String.valueOf(fileSize), Field.Store.YES);
// 创建文档对象
Document document = new Document();
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContent);
document.add(fieldSize);
// 4. 创建field对象,将field添加到document对象中。
// 5. 使用IndexWriter对象将document对象写入索引库,此过程进行索引创建,并将索引和document对象写入索引库。
indexWriter.addDocument(document);
} 最后关闭即可:
indexWriter.close(); 完整代码:
package com.peterjxl.lucene;
import org.apache.commons.io.FileUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;
import java.io.File;
public class LuceneFirst {
@Test
public void createIndex() throws Exception {
// 1. 创建一个Director对象,指定索引库保存的位置。
Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
// 2. 基于Directory对象创建一个IndexWriter对象(用来写索引)
IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig());
// 3. 读取磁盘上的文件,对应每个文件创建一个文档对象。
File dir = new File("D:\\temp\\searchsource");
File[] files = dir.listFiles();
for(File file : files) {
String fileName = file.getName();
String filePath = file.getPath();
String fileContent = FileUtils.readFileToString(file, "utf-8");
long fileSize = FileUtils.sizeOf(file);
// 4. 创建Field
// 参数1:域的名称, 参数2:域的内容, 参数3:是否存储
Field fieldName = new TextField("name", fileName, Field.Store.YES);
Field fieldPath = new TextField("path", filePath, Field.Store.YES);
Field fieldContent = new TextField("content", fileContent, Field.Store.YES);
Field fieldSize = new TextField("size", String.valueOf(fileSize), Field.Store.YES);
// 4. 将field添加到document对象中。
Document document = new Document();
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContent);
document.add(fieldSize);
// 5. 使用IndexWriter对象将document对象写入索引库,此过程进行索引创建,并将索引和document对象写入索引库。
indexWriter.addDocument(document);
}
// 6. 关闭IndexWriter对象。
indexWriter.close();
}
} 我们运行,然后打开 D:\temp\index,可以看到有一些索引文件:
索引文件是二进制文件,如果我们想直接看是不行的;我们可以借住一些工具来查看,例如 Luke。Luke 是一个用于 Lucene/Solr/Elasticsearch 搜索引擎的,方便开发和诊断的 GUI(可视化)工具。
Luke 的 GitHub 地址:DmitryKey/luke,这里我们使用 7.4.0 的,可以去 GitHub 上下载或去我的 网盘 上下载,相关路径为 Java相关/06.主流框架/10.Lucene
注意:
运行后,我们选择索引所在的位置,然后确定:
然后我们就可以看到索引库的概况:

在 Documents 选修课,还能看到每个文档的内容。例如目前是 0 号文档,下方则是该文档的各个字段(之前我们设置了 Field.Store 为 Yes,所以存储了)
还有 search 标签,可以试试搜索功能:

之后的 Luke 的其他选项卡就用的较少,这里就不一一介绍了。
接下来我们继续写代码,实现查询索引的功能。步骤:
@Test
public void searchIndex() throws Exception {
// 1. 第一步:创建一个Directory对象,也就是索引库存放的位置。
Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
// 2. 第二步:创建一个indexReader对象,需要指定Directory对象。
IndexReader indexReader = DirectoryReader.open(directory);
// 3. 第三步:创建一个indexsearcher对象,需要指定IndexReader对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 4. 第四步:创建一个TermQuery对象,指定查询的域和查询的关键词。
Query query = new TermQuery(new Term("content", "spring"));
// 5. 第五步:执行查询。得到一个TopDocs对象,包含查询结果的总记录数,和文档列表
// 参数1:查询对象 参数2:查询结果返回的最大记录数
TopDocs topDocs = indexSearcher.search(query, 10);
System.out.println("查询出来的总记录数:" + topDocs.totalHits);
// 6. 第六步:返回查询结果。遍历查询结果并输出。
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
// 取文档id
int docId = scoreDoc.doc;
// 根据id取文档对象
Document document = indexSearcher.doc(docId);
// 取文档的属性
System.out.println("name: " + document.get("name"));
System.out.println("path: " + document.get("path"));
System.out.println("size: " + document.get("size"));
//System.out.println("content: " + document.get("content"));
System.out.println("-------------分割线-----------------");
}
// 7. 第七步:关闭IndexReader对象
indexReader.close();
} 为了方便,我们不输出文档的内容,太多了。运行结果:
查询出来的总记录数:6
name: spring_README.txt
path: D:\temp\searchsource\spring_README.txt
size: 3257
-------------分割线-----------------
name: springmvc.txt
path: D:\temp\searchsource\springmvc.txt
size: 2124
-------------分割线-----------------
name: 2.Serving Web Content.txt
path: D:\temp\searchsource\2.Serving Web Content.txt
size: 35
-------------分割线-----------------
name: 1.create web page.txt
path: D:\temp\searchsource\1.create web page.txt
size: 47
-------------分割线-----------------
name: spring.txt
path: D:\temp\searchsource\spring.txt
size: 82
-------------分割线-----------------
name: cxf_README.txt
path: D:\temp\searchsource\cxf_README.txt
size: 3770
-------------分割线-----------------已将源码上传到 Gitee 或 GitHub 上。并且创建了分支 demo1,读者可以通过切换分支来查看本文的示例代码